package com.belizer.pay.service.impl;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.belizer.pay.service.WxPayService;
import com.belizer.pay.utils.BeanUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.app.AppService;
import com.wechat.pay.java.service.payments.app.AppServiceExtension;
import com.wechat.pay.java.service.payments.app.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.payments.app.model.QueryOrderByIdRequest;
import com.wechat.pay.java.service.payments.app.model.QueryOrderByOutTradeNoRequest;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.payments.nativepay.model.Amount;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.*;
import com.wechat.pay.java.service.transferbatch.TransferBatchService;
import com.wechat.pay.java.service.transferbatch.model.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Service
public class WxPayServiceImpl implements WxPayService {

    @Resource
    private Config config;
    @Value("${wxpay.appId}")
    private String appId;
    @Value("${wxpay.appSecret}")
    private String appSecret;

    @Value("${wxpay.merchantId}")
    private String merchantId;

    public String nativePay(String description,String orderId,Integer totalFee) {
        // 构建service
        NativePayService service = new NativePayService.Builder().config(config).build();
        // request.setXxx(val)设置所需参数，具体参数可见Request定义
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal(totalFee);
        request.setAmount(amount);
        request.setAppid(appId);
        request.setMchid(merchantId);
        request.setDescription(description);
        request.setNotifyUrl("");
        request.setOutTradeNo(orderId);
        // 调用下单方法，得到应答
        PrepayResponse response = service.prepay(request);
        // 使用微信扫描 code_url 对应的二维码，即可体验Native支付
        return response.getCodeUrl();
    }

    public Map<String, Object> appPay(String description, String orderId, Integer totalFee) {
        AppServiceExtension service = new AppServiceExtension.Builder().config(config).build();

        com.wechat.pay.java.service.payments.app.model.PrepayRequest request = new com.wechat.pay.java.service.payments.app.model.PrepayRequest();
        com.wechat.pay.java.service.payments.app.model.Amount amount = new com.wechat.pay.java.service.payments.app.model.Amount();
        amount.setTotal(totalFee);
        request.setAmount(amount);
        request.setAppid(appId);
        request.setMchid(merchantId);
        request.setDescription(description);
        request.setNotifyUrl("");
        request.setOutTradeNo(orderId);

        PrepayWithRequestPaymentResponse prepayWithRequestPaymentResponse = service.prepayWithRequestPayment(request);

        return BeanUtils.beanToMap(prepayWithRequestPaymentResponse);
    }

    public Map<String,Object> queryOrderByTransactionId(String transactionId) {
        Map<String,Object> res = null;
        try {
            AppService appService  = new AppService.Builder().config(config).build();

            QueryOrderByIdRequest queryOrderByIdRequest = new QueryOrderByIdRequest();
            queryOrderByIdRequest.setTransactionId(transactionId);
            queryOrderByIdRequest.setMchid(merchantId);

            Transaction transaction = appService.queryOrderById(queryOrderByIdRequest);
            res = BeanUtils.beanToMap(transaction);
        }catch (ServiceException e) {
            // API返回失败, 例如ORDER_NOT_EXISTS
            System.out.printf("code=[%s], message=[%s]\n", e.getErrorCode(), e.getErrorMessage());
            System.out.printf("reponse body=[%s]\n", e.getResponseBody());
        }

        return res;
    }

    public Map<String,Object> queryOrderByOutTradeNo(String orderId) {
        Map<String,Object> res = null;
        try {
            AppService appService  = new AppService.Builder().config(config).build();

            QueryOrderByOutTradeNoRequest queryOrderByOutTradeNoRequest = new QueryOrderByOutTradeNoRequest();
            queryOrderByOutTradeNoRequest.setOutTradeNo(orderId);
            queryOrderByOutTradeNoRequest.setMchid(merchantId);

            Transaction transaction = appService.queryOrderByOutTradeNo(queryOrderByOutTradeNoRequest);
            res = BeanUtils.beanToMap(transaction);
        }catch (ServiceException e) {
            // API返回失败, 例如ORDER_NOT_EXISTS
            System.out.printf("code=[%s], message=[%s]\n", e.getErrorCode(), e.getErrorMessage());
            System.out.printf("reponse body=[%s]\n", e.getResponseBody());
        }

        return res;
    }

    public ResponseEntity handleCallback(HttpServletRequest request) {
        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                //微信支付平台证书的序列号，验签必须使用序列号对应的微信支付平台证书
                .serialNumber(request.getHeader("Wechatpay-Serial"))
                //签名中的随机数
                .nonce(request.getHeader("Wechatpay-Nonce"))
                //应答的微信支付签名
                .signature(request.getHeader("Wechatpay-Signature"))
                .timestamp(request.getHeader("Wechatpay-Timestamp"))
                .body(getBody(request))
                .build();

// 如果已经初始化了 RSAAutoCertificateConfig，可直接使用
// 没有的话，则构造一个
//        NotificationConfig config = new RSAAutoCertificateConfig.Builder()
//                .merchantId(merchantId)
//                .privateKeyFromPath(privateKeyPath)
//                .merchantSerialNumber(merchantSerialNumber)
//                .apiV3Key(apiV3Key)
//                .build();

// 初始化 NotificationParser
        NotificationParser parser = new NotificationParser((NotificationConfig)config);

        Transaction transaction = null;
        try {
            // 以支付通知回调为例，验签、解密并转换成 Transaction
            transaction = parser.parse(requestParam, Transaction.class);
        } catch (ValidationException e) {
            // 签名验证失败，返回 401 UNAUTHORIZED 状态码
            e.printStackTrace();
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }

// 如果处理失败，应返回 4xx/5xx 的状态码，例如 500 INTERNAL_SERVER_ERROR
        if (handleBussiness(transaction)) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }

// 处理成功，返回 200 OK 状态码
        return ResponseEntity.status(HttpStatus.OK).build();

    }

    private boolean handleBussiness(Transaction transaction) {
        if(Objects.isNull(transaction)) return false;
        //todo 处理业务逻辑

        return false;
    }

    private String getBody(HttpServletRequest request) {
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader reader = null;
        String line;
        try {
            reader = request.getReader();
            while (null != (line = reader.readLine())) {
                stringBuilder.append(line);
            }
        }catch (Exception e) {
            return null;
        }finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return stringBuilder.toString();
    }

    public Map<String,Object> refund(String transactionId,String orderId,Long refundAmount,Long orderAmount) {
        Map<String, Object> res = null;
        try {
            RefundService refundService  = new RefundService.Builder().config(config).build();
            CreateRequest createRequest = new CreateRequest();
            createRequest.setTransactionId(transactionId);
            createRequest.setOutRefundNo("退款失败，再次发起退款要保证OutRefundNo相同");
            createRequest.setOutTradeNo(orderId);

            AmountReq amountReq = new AmountReq();
            amountReq.setRefund(refundAmount);
            amountReq.setTotal(orderAmount);

            createRequest.setAmount(amountReq);

            Refund refund = refundService.create(createRequest);

            res = BeanUtils.beanToMap(refund);
        }catch (HttpException e) { // 发送HTTP请求失败
            // 调用e.getHttpRequest()获取请求打印日志或上报监控，更多方法见HttpException定义
        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300，例如500
            // 调用e.getResponseBody()获取返回体打印日志或上报监控，更多方法见ServiceException定义
        } catch (MalformedMessageException e) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
            // 调用e.getMessage()获取信息打印日志或上报监控，更多方法见MalformedMessageException定义
        }

        return res;
    }

    public ResponseEntity handleRefuseCallback(HttpServletRequest request) {
        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                //微信支付平台证书的序列号，验签必须使用序列号对应的微信支付平台证书
                .serialNumber(request.getHeader("Wechatpay-Serial"))
                //签名中的随机数
                .nonce(request.getHeader("Wechatpay-Nonce"))
                //应答的微信支付签名
                .signature(request.getHeader("Wechatpay-Signature"))
                .timestamp(request.getHeader("Wechatpay-Timestamp"))
                .body(getBody(request))
                .build();

// 如果已经初始化了 RSAAutoCertificateConfig，可直接使用
// 没有的话，则构造一个
//        NotificationConfig config = new RSAAutoCertificateConfig.Builder()
//                .merchantId(merchantId)
//                .privateKeyFromPath(privateKeyPath)
//                .merchantSerialNumber(merchantSerialNumber)
//                .apiV3Key(apiV3Key)
//                .build();

// 初始化 NotificationParser
        NotificationParser parser = new NotificationParser((NotificationConfig)config);

        RefundNotification refundNotification = null;
        try {
            // 以支付通知回调为例，验签、解密并转换成 Transaction
            refundNotification = parser.parse(requestParam, RefundNotification.class);
        } catch (ValidationException e) {
            // 签名验证失败，返回 401 UNAUTHORIZED 状态码
            e.printStackTrace();
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }

// 如果处理失败，应返回 4xx/5xx 的状态码，例如 500 INTERNAL_SERVER_ERROR
        if (handleRefundBussiness(refundNotification)) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }

// 处理成功，返回 200 OK 状态码
        return ResponseEntity.status(HttpStatus.OK).build();

    }

    private boolean handleRefundBussiness(RefundNotification refundNotification) {
        if (Objects.isNull(refundNotification)) return false;

        //todo 处理业务逻辑


        return false;
    }

    public Map<String,Object> queryRefund(String outRefundNo) {
        Map<String, Object> res = null;
        try {
            RefundService refundService  = new RefundService.Builder().config(config).build();

            QueryByOutRefundNoRequest queryByOutRefundNoRequest = new QueryByOutRefundNoRequest();
            queryByOutRefundNoRequest.setOutRefundNo(outRefundNo);

            Refund refund = refundService.queryByOutRefundNo(queryByOutRefundNoRequest);

            res = BeanUtils.beanToMap(refund);
        }catch (HttpException e) { // 发送HTTP请求失败
            // 调用e.getHttpRequest()获取请求打印日志或上报监控，更多方法见HttpException定义
        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300，例如500
            // 调用e.getResponseBody()获取返回体打印日志或上报监控，更多方法见ServiceException定义
        } catch (MalformedMessageException e) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
            // 调用e.getMessage()获取信息打印日志或上报监控，更多方法见MalformedMessageException定义
        }

        return res;
    }

    //https://api.weixin.qq.com/sns/oauth2/
    // access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
    public Map<String,Object> getAccessToken(String authorizationCode) {
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?";
        url += ("appid=" + appId + "&secret=" + appSecret + "&code="
                + authorizationCode + "&grant_type=authorization_code");

        HttpRequest get = HttpUtil.createGet(url);
        HttpResponse httpResponse = get.execute();
        Map<String,Object> res = null;
        if (httpResponse.isOk()) {
            String body = httpResponse.body();
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                res = objectMapper.readValue(body, new TypeReference<Map<String, Object>>() {});
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        return res;
    }

    //GET https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID
    public Map<String,Object> getUserInfo(String accessToken,String openid) {
        String url = "https://api.weixin.qq.com/sns/userinfo?";
        url += ("access_token=" + accessToken + "&openid=" + openid);

        HttpRequest get = HttpUtil.createGet(url);
        HttpResponse httpResponse = get.execute();
        Map<String,Object> res = null;
        if (httpResponse.isOk()) {
            String body = httpResponse.body();
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                res = objectMapper.readValue(body, new TypeReference<Map<String, Object>>() {});
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        return res;
    }

    public Map<String,Object> singleTransfer(String withdrawNo,Long amount,String openid,String userName) {
        Map<String,Object> res = null;
        try {
            TransferBatchService service = new TransferBatchService.Builder().build();

            InitiateBatchTransferRequest request = new InitiateBatchTransferRequest();
            request.setAppid(appId);
            request.setBatchName("服务费");
            request.setBatchRemark("服务费");
            request.setTotalAmount(amount);
            request.setOutBatchNo(withdrawNo);
            request.setTotalNum(1);

            List<TransferDetailInput> transferDetailInputList = new ArrayList<>(1);
            TransferDetailInput transferDetailInput = new TransferDetailInput();
            transferDetailInput.setOutDetailNo(withdrawNo);
            transferDetailInput.setTransferAmount(amount);
            transferDetailInput.setTransferRemark("服务费");
            transferDetailInput.setOpenid(openid);

            transferDetailInput.setUserName(userName);
            transferDetailInputList.add(transferDetailInput);

            request.setTransferDetailList(transferDetailInputList);

            InitiateBatchTransferResponse initiateBatchTransferResponse = service.initiateBatchTransfer(request);
            res = BeanUtils.beanToMap(initiateBatchTransferResponse);
        }catch (HttpException e) { // 发送HTTP请求失败
            // 调用e.getHttpRequest()获取请求打印日志或上报监控，更多方法见HttpException定义
        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300，例如500
            // 调用e.getResponseBody()获取返回体打印日志或上报监控，更多方法见ServiceException定义
        } catch (MalformedMessageException e) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
            // 调用e.getMessage()获取信息打印日志或上报监控，更多方法见MalformedMessageException定义
        }

        return res;
    }

    // 获取单笔转账状态，通过微信平台批次号和微信平台明细号
    public Map<String,Object> getTransferDetailByNo(String wxBatchId,String wxDetailId) {
        Map<String,Object> res = null;
        try {
            TransferBatchService service = new TransferBatchService.Builder().build();

            GetTransferDetailByNoRequest request = new GetTransferDetailByNoRequest();
            request.setBatchId(wxBatchId);
            request.setDetailId(wxDetailId);

            TransferDetailEntity transferDetailByNo = service.getTransferDetailByNo(request);
            res = BeanUtils.beanToMap(transferDetailByNo);
        }catch (HttpException e) { // 发送HTTP请求失败
            // 调用e.getHttpRequest()获取请求打印日志或上报监控，更多方法见HttpException定义
        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300，例如500
            // 调用e.getResponseBody()获取返回体打印日志或上报监控，更多方法见ServiceException定义
        } catch (MalformedMessageException e) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
            // 调用e.getMessage()获取信息打印日志或上报监控，更多方法见MalformedMessageException定义
        }

        return res;
    }

    // 获取单笔转账状态,通过商户转账流水号
    public Map<String,Object> getTransferDetailByNo(String withdrawNo) {
        Map<String,Object> res = null;
        try {
            TransferBatchService service = new TransferBatchService.Builder().build();

            GetTransferDetailByOutNoRequest request = new GetTransferDetailByOutNoRequest();
            request.setOutBatchNo(withdrawNo);
            request.setOutDetailNo(withdrawNo);

            TransferDetailEntity transferDetailByNo = service.getTransferDetailByOutNo(request);
            res = BeanUtils.beanToMap(transferDetailByNo);
        }catch (HttpException e) { // 发送HTTP请求失败
            // 调用e.getHttpRequest()获取请求打印日志或上报监控，更多方法见HttpException定义
        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300，例如500
            // 调用e.getResponseBody()获取返回体打印日志或上报监控，更多方法见ServiceException定义
        } catch (MalformedMessageException e) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
            // 调用e.getMessage()获取信息打印日志或上报监控，更多方法见MalformedMessageException定义
        }

        return res;
    }

}
